<?php
declare (strict_types=1);

namespace addons\Concurrency\driver\channels;

use addons\Concurrency\exception\ConcurrencyException;

class Lua
{

    protected $redis;

    protected $config;

    protected $key;

    public function __construct($key = '')
    {
        if (empty($key)) throw new ConcurrencyException('Missing key');
        $this->config = config('concurrency.channels.lua');
        $this->redis = Redis::make($this->config);
        $this->key = $key;
    }

    public function getKeys(int $id, string $specs = '')
    {
        return $this->key . ':' . ($specs ? $id . ':' . urlencode($specs) : $id);
    }

    /**
     * 获取数量
     * @param int $id
     * @param string $specs
     * @return string|null
     */
    public function get(int $id, string $specs = '')
    {
        return $this->redis->get($this->getKeys($id, $specs));
    }

    /**
     * 删除缓存
     * @param int $id
     * @param string $specs
     * @return string|null
     */
    public function delete(int $id, string $specs = '')
    {
        return $this->redis->delete($this->getKeys($id, $specs));
    }

    /**
     * 更新数量
     * @param int $id
     * @param string $specs
     * @param int|string $value
     * @return void
     * @throws ConcurrencyException
     */
    public function update(int $id, string $specs = '', int|string $value = '')
    {
        try {
            $this->redis->set($this->getKeys($id, $specs), $value);
        } catch (\Exception $e) {
            throw new ConcurrencyException($e->getMessage());
        }
    }

    /**
     * 主动增加数量
     * @param int $id
     * @param string $specs
     * @param int|string $value
     * @return void
     * @throws ConcurrencyException
     */
    public function add(int $id, string $specs = '', int|string $value = '')
    {
        try {
            $this->redis->incr($this->getKeys($id, $specs), $value);
        } catch (\Exception $e) {
            throw new ConcurrencyException($e->getMessage());
        }
    }

    /**
     * 主动减少数量
     * @param int $id
     * @param string $specs
     * @param int|string $value
     * @return void
     * @throws ConcurrencyException
     */
    public function minus(int $id, string $specs = '', int|string $value = '')
    {
        try {
            $this->redis->decr($this->getKeys($id, $specs), $value);
        } catch (\Exception $e) {
            throw new ConcurrencyException($e->getMessage());
        }
    }

    /**
     * 并发减少数量
     * @param int $id
     * @param string $specs
     * @param int $num
     * @return bool
     * @throws ConcurrencyException
     */
    public function leave(int $id, string $specs = '', int $num = 1): bool
    {
        try {
            $result = $this->redis->eval($this->getKeys($id, $specs), $num);
            return $result >= 0 ? true : false;
        } catch (\Exception $e) {
            throw new ConcurrencyException($e->getMessage());
        }
    }
}